%{
#include <ctype.h>
#include <string.h>
#include <stdio.h>        // for printf in main

#include "errmsg.h"
#include "utils.h"


typedef struct bison_pipe_ {
		int pos;
    int ival;
    double dval;
    string_t str;
    char chr;
		bool bval;
} bison_pipe;

static bison_pipe yylval;

static int _char_pos = 1;

int yywrap(void);

#define ADJ \
do \
{ \
    em_tok_pos = yylval.pos = _char_pos; \
    _char_pos += yyleng; \
} \
while (0)

static int _comment_level = 0;

#define INIT_LEN 32
static string_t _str_buf;
static int _str_len;
static int _str_cap;
static void init_buf(void);
static void append_char(char ch);


enum yytokentype {
		SEMICOLON = 1, LPARAN, RPARAN, LBRACE, RBRACE,
		LBRACK, RBRACK, DOT, COMMA, ASSIGN,
    AND, OR, LT, GT, LE, GE,
    EQ, NEQ, NOT, PLUS, MINUS, TIMES,
    DIVIDE, IMPORT, CLASS, INT, FLOAT,
    CHAR, STRING, BOOLEAN, IF, ELSE,
    WHILE, RETURN, THIS, NEW, INT_LIT, COLON, 
		FLOAT_LIT, IDF, STRING_LIT, CHAR_LIT, BOOLEAN_LIT, LENGTH,
};


%}

%option nounput

%x COMMENT STRING CHAR

%%

[ \t\f\v\r]             { ADJ; }
\n                      { ADJ; em_newline(); }

"/*"                    { ADJ; _comment_level = 1; BEGIN(COMMENT); }
<COMMENT>[^*/\n]*       { ADJ; }
<COMMENT>"*"+[^*/\n]*   { ADJ; }
<COMMENT>"/"+[^*/\n]*   { ADJ; }
<COMMENT>\n             { ADJ; em_newline(); }
<COMMENT>"/"+"*"        { ADJ; _comment_level++; }
<COMMENT>"*"+"/"        {
    ADJ;
    _comment_level--;
    if (_comment_level <= 0)
        BEGIN(INITIAL);
}

"//"(.)*\n              { ADJ; em_newline(); }

";"                     { ADJ; return SEMICOLON; }
":"											{ ADJ; return COLON; }
"("                     { ADJ; return LPARAN; }
")"                     { ADJ; return RPARAN; }
"{"                     { ADJ; return LBRACE; }
"}"                     { ADJ; return RBRACE; }
"["                     { ADJ; return LBRACK; }
"]"                     { ADJ; return RBRACK; }
"."                     { ADJ; return DOT; }
","                     { ADJ; return COMMA; }
"="                     { ADJ; return ASSIGN; }
"&&"                    { ADJ; return AND; }
"||"                    { ADJ; return OR; }
"<"                     { ADJ; return LT; }
">"                     { ADJ; return GT; }
"<="                    { ADJ; return LE; }
">="                    { ADJ; return GE; }
"=="                    { ADJ; return EQ; }
"!="                    { ADJ; return NEQ; }
"!"                     { ADJ; return NOT; }
"+"                     { ADJ; return PLUS; }
"-"                     { ADJ; return MINUS; }
"*"                     { ADJ; return TIMES; }
"/"                     { ADJ; return DIVIDE; }


import                  { ADJ; return IMPORT; }
length									{ ADJ; return LENGTH; }
class                   { ADJ; return CLASS; }
int                     { ADJ; return INT; }
float                   { ADJ; return FLOAT; }
char                    { ADJ; return CHAR; }
string                  { ADJ; return STRING; }
boolean                 { ADJ; return BOOLEAN; }
if                      { ADJ; return IF; }
else                    { ADJ; return ELSE; }
while                   { ADJ; return WHILE; }
return                  { ADJ; return RETURN; }
this                    { ADJ; return THIS; }
new                     { ADJ; return NEW; }
true                    { ADJ; yylval.bval = true; return BOOLEAN_LIT; }
false                   { ADJ; yylval.bval = false; return BOOLEAN_LIT; }

[0-9]+                  { ADJ; yylval.ival = atoi(yytext); return INT_LIT; }
[0-9]+"."[0-9]+         { ADJ; yylval.dval = atof(yytext); return FLOAT_LIT; }
[_a-zA-Z][_a-zA-Z0-9]*  { ADJ; yylval.str = string(yytext); return IDF; }

\"                      { ADJ; init_buf(); BEGIN(STRING); }
<STRING>\"              {
    ADJ;
    BEGIN(INITIAL);
    yylval.str = _str_buf;
    return STRING_LIT;
}
<STRING>\n              { ADJ; em_newline(); }
<STRING>\\[0-9]{3}      {
    int result;

    ADJ;
    sscanf(yytext + 1, "%d", &result);
    if (result > 0xFF)
        em_error(em_tok_pos, "character out of range");
    append_char(result);
}
<STRING>\\n             { ADJ; append_char('\n'); }
<STRING>\\t             { ADJ; append_char('\t'); }
<STRING>\\\"            { ADJ; append_char('\"'); }
<STRING>\\\\            { ADJ; append_char('\\'); }
<STRING>\\(.|\n)        { ADJ; append_char(yytext[1]); }
<STRING>[^\\\n\"]+      {
    char *p = yytext;

    ADJ;
    while (*p)
        append_char(*p++);
}



"'"                     { ADJ; BEGIN(CHAR); }
<CHAR>"'"               {
    ADJ;
    BEGIN(INITIAL);
    em_error(em_tok_pos, "empty character literal");
}

<CHAR>\n                {
    ADJ;
    BEGIN(INITIAL);
    em_error(em_tok_pos, "missing terminating ' character");
    em_newline();
}
<CHAR>\\[0-9]{3}"'"     {
    int result;

    ADJ;
    BEGIN(INITIAL);
    sscanf(yytext + 1, "%d", &result);
    if (result > 0xFF || result == 39 || result == 92)
        em_error(em_tok_pos, "character out of range");
    else {
      yylval.chr = result;
      return CHAR_LIT;
    }
}
<CHAR>\\n"'"          {
    ADJ;
    BEGIN(INITIAL);
    yylval.chr = '\n';
    
    return CHAR_LIT;
}
<CHAR>\\t"'"          {
    ADJ;
    BEGIN(INITIAL);
    yylval.chr = '\t';
    
    return CHAR_LIT;
}
<CHAR>\\\""'"          {
    ADJ;
    BEGIN(INITIAL);
    yylval.chr = '\"';
    
    return CHAR_LIT;
}
<CHAR>\\\\"'"          {
    ADJ;
    BEGIN(INITIAL);
    yylval.chr = '\\';
    
    return CHAR_LIT;
}
<CHAR>\\\n             { ADJ; em_newline(); }
<CHAR>[^\\\n]"'"       {
    ADJ;
    BEGIN(INITIAL);
    yylval.chr = *yytext;
    
    return CHAR_LIT;
}

.                      { ADJ; em_error(em_tok_pos, "illegal token"); }

%%

int yywrap(void)
{
    _char_pos = 1;
    return 1;
}

static void init_buf(void)
{
    _str_buf = checked_malloc(INIT_LEN);
    _str_buf[0] = 0;
    _str_len = 0;
    _str_cap = INIT_LEN;
}

static void append_char(char ch)
{
    if (++_str_len == _str_cap)
    {
        char *p;

        _str_cap *= 2;
        p = checked_malloc(_str_cap);
        memcpy(p, _str_buf, _str_len);
        free(_str_buf);
        _str_buf = p;
    }
    _str_buf[_str_len - 1] = ch;
    _str_buf[_str_len] = 0;
}

int main(int argc, char **argv)
{
  int token;
  
	string_t TOKEN_NAME[] = {
		"SEMICOLON", "LPARAN", "RPARAN", "LBRACE", "RBRACE",
		"LBRACK", "RBRACK", "DOT", "COMMA", "ASSIGN",
    "AND", "OR", "LT", "GT", "LE", "GE",
    "EQ", "NEQ", "NOT", "PLUS", "MINUS", "TIMES",
    "DIVIDE", "IMPORT", "CLASS", "INT", "FLOAT",
    "CHAR", "STRING", "BOOLEAN", "IF", "ELSE",
    "WHILE", "RETURN", "THIS", "NEW", "INT_LIT", "COLON",
		"FLOAT_LIT", "IDF", "STRING_LIT", "CHAR_LIT", "BOOLEAN_LIT", "LENGTH"
	};

	FILE *f = fopen(argv[1], "r");

	if(!f) {
		fprintf(stderr, "Can't Open File\n");
		exit(-1);
	}

	yyrestart(f);

	while((token = yylex())) {
	   printf("%s\n", TOKEN_NAME[token-1]);
	}

	fclose(f);
	
	printf("\n");

	return 0;
}
